package com.zn.znch.yhp.znchv2.service;


import com.zn.znch.yhp.znchv2.common.DateUtil;
import com.zn.znch.yhp.znchv2.domain.DTO.PointData;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.math3.fitting.PolynomialCurveFitter;
import org.apache.commons.math3.fitting.WeightedObservedPoints;
import org.apache.commons.math3.stat.regression.SimpleRegression;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.io.*;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Component
public class DCModelUpdate {
    public static String path = "D:/temp/znch/dc/data";
    public static String configPath = "D:/temp/znch/dc/config/config.properties";
    public static String configTime = "D:/temp/znch/dc/config/time.properties";
//    public static String path = "/home/zd/znch/dc/data";
//    public static String configPath = "/home/zd/znch/dc/config/config.properties";
//    public static String configTime = "/home/zd/znch/dc/config/time.properties";



    public static String F_S="FUHE_START";
    public static String F_E="FUHE_END";
    public static String N_S="NOFUHE_START";
    public static String N_E="NOFUHE_END";

    public static int FUHE_START = 8;
    public static int FUHE_END = 6;
    public static int NOFUHE_START = 2;
    public static int NOFUHE_END = 1;

    public static Map<String,Double> alfaMap =new HashMap();


    public static void main(String[] args) {
        DCModelUpdateTask();
    }
    //@Scheduled(cron= "0 30 0 1,11,21 * ?")
    @Scheduled(cron= "0 30 0 * * ?")
    public static void DCModelUpdateTask() {
        log.info("短吹模型更新任务开始");
        try {
            FileInputStream fis = new FileInputStream(configTime);
            Properties props = new Properties();
            props.load(fis);
            FUHE_START = new Integer(props.get(F_S).toString());
            FUHE_END = new Integer(props.get(F_E).toString());
            NOFUHE_START = new Integer(props.get(N_S).toString());
            NOFUHE_END = new Integer(props.get(N_E).toString());
            //加载目录下所有的数据文件
            File directory = new File(path);
            String[] FileList = directory.list();
            Map<String,String> map = new HashMap<>();
            for (int i = 0; i < FileList.length; i++) {
                String filePath = directory.getAbsolutePath()+"\\"+FileList[i];
                String key = FileList[i].substring(0,12);
                log.info("短吹模型更新任务，{}数据计算",key);
                String data = getParameters(filePath,key);
                Double alfa = alfaMap.get(key);
                if(data ==null ){
                    continue;
                }
                map.put(key,data+","+alfa);
            }


            FileInputStream config = new FileInputStream(configPath);
            Properties properties = new Properties();
            properties.load(config);
            for (String key:map.keySet()) {
                // 设置属性值
                properties.setProperty(key, map.get(key));
            }
            FileOutputStream fos = new FileOutputStream(configPath);
            properties.store(fos, "Generated configuration properties file");
            fos.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        log.info("短吹模型更新任务完成");

    }


    public static String getParameters(String Filepath,String key) throws IOException {
        File file = new File(Filepath);
        FileReader fr = new FileReader(file);
        BufferedReader br = new BufferedReader(fr);
        String line = br.readLine();
        List<PointData> pointDatas = new ArrayList<>();
        List<PointData> pointList = new ArrayList<>();
        int num = 0;
        while (line != null) {
            String[] lineStr = line.split(",");
            if(Double.parseDouble(lineStr[1])>400){
                num++;
            }
            PointData pointData = new PointData(lineStr[0],Double.parseDouble(lineStr[1]),
                    Double.parseDouble(lineStr[2]),Integer.valueOf(lineStr[3]),Double.parseDouble(lineStr[4]));
            pointDatas.add(pointData);
            pointList.add(pointData);
            line = br.readLine();
        }

//        if (num<=30000){
//            log.info("当前文件{}有效数据不满足30000，暂停更新模型参数",Filepath);
//            return null;
//        }
        //按照负荷从小到大排序
        Collections.sort(pointDatas, new Comparator<PointData>() {
            @Override
            public int compare(PointData o1, PointData o2) {
                return Double.compare(o1.getFuhe(), o2.getFuhe());
            }
        });
        //按负荷对数据进行切片,切片后按不同列降序
        List<List<PointData>> xunlianData = new ArrayList();
        for (int i = 0; i < pointDatas.size()-1; ) {
            List<PointData> list = new ArrayList<>();
            PointData start = pointDatas.get(i);
            list.add(start);
            PointData next = pointDatas.get(i+1);
            i++;
            while ((next.getFuhe() - start.getFuhe()<=5) && i < pointDatas.size()-1){
                list.add(next);
                i++;
                next = pointDatas.get(i);
            }

            xunlianData.add(list);
        }
        List<PointData> result = new ArrayList<>();
        for (int i = 0; i < xunlianData.size(); i++) {
            List<PointData>  list= xunlianData.get(i);
            //按照壁温从大到小排序
            Collections.sort(list, new Comparator<PointData>() {
                @Override
                public int compare(PointData o1, PointData o2) {
                    return Double.compare(o2.getBiwen(),o1.getBiwen());
                }
            });
            //获取前15%，不足+1
            int count = new Double(list.size()*0.15).intValue() +1;
            list = list.subList(0,count);
            result.addAll(list);
            ;        }
        String xishu  = quadraticFitting(result);
        if(xishu.endsWith("2")){
            //拟合效果好
            alfaMap.put(key,new Double(1));
            method2(pointList,xishu,key);
        }else{
            alfaMap.put(key,new Double(2));
            method1(pointList,key);
        }
        return xishu;

    }


    //计算一元二次系数
    public static String quadraticFitting(List<PointData>  list){
        double[] xData = new double[list.size()];
        double[] yData = new double[list.size()];
        for (int i = 0; i < list.size(); i++) {
            xData[i] = list.get(i).getFuhe();
            yData[i] = list.get(i).getBiwen();
        }

        WeightedObservedPoints points = new WeightedObservedPoints();
        for (int i = 0; i < xData.length; i++) {
            points.add(xData[i], yData[i]);
        }

        // 进行一元二次拟合
        PolynomialCurveFitter fitter = PolynomialCurveFitter.create(2);
        double[] params = fitter.fit(points.toList());

        double[] yDataYuche = new double[list.size()];
        for (int i = 0; i < xData.length; i++) {
            double x = xData[i];
            double y = x*x*params[2]+x*params[1]+params[0];
            yDataYuche[i] = y;
        }

        SimpleRegression regression = new SimpleRegression();
        for (int i = 0; i < xData.length; i++) {
            regression.addData(yDataYuche[i], yData[i]);
        }
        double rSquared = regression.getRSquare();
        Integer flag = new Integer(0);
        if(rSquared >= 0.85){
            flag = 2;
        }else{
            flag = 3;
        }
        StringBuffer sb =new StringBuffer();
        sb.append(params[2]).append(",").append(params[1]).append(",").append(params[0]).append(",").append(flag);
        return sb.toString();
    }

    /**
     * 拟合效果不好
     * @param pointList
     */
    private static double method1( List<PointData> pointList,String key) {
        double biwenMax = pointList.stream().max(Comparator.comparingDouble(PointData::getBiwen)).get().getBiwen();
        double biwenMin = pointList.stream().min(Comparator.comparingDouble(PointData::getBiwen)).get().getBiwen();
        Double alfa = alfaMap.get(key);
        double dt = (biwenMax-biwenMin)/alfa;
        int start = 0;
        Map<Integer,PointData> maxList = new HashMap<>();
        for (int i = 0; i < pointList.size()-1; ) {
            PointData pointMax = pointList.get(i);
            int maxIndex = i;
            PointData next = pointList.get(i + 1);
            if ((pointMax.getChuihui() == 1 || pointMax.getChuihui() == 2 || pointMax.getChuihui() == 3) && next.getChuihui() == 0) {
                //获取接下去30行最大的温度
                int j = 0;
                for (; j < 30; j++) {
                    if (i + j == pointList.size() - 1) {
                        break;
                    }
                    if (next.getChuihui() != 1 && next.getChuihui() != 2&& next.getChuihui() != 3) {
                        if(pointMax.getBiwen() <= next.getBiwen()){
                            pointMax = next;
                        }
                        if(maxList.size()==0 || start ==0){
                            start = i+1 ;
                            maxIndex = i+1;
                        }

                    }
                    next = pointList.get(j + i);
                }
                i = i + j;
                maxList.put(maxIndex,pointMax);
            }else {
                i++;
            }
        }

        PointData pointMax = null;
        Set<Integer> keys = maxList.keySet();
        int count = 0 ;
        for (int i = start; i < pointList.size(); ) {
            if(keys.contains(i)){
                pointMax = maxList.get(i);
            }
            //短吹后第一个点
            PointData pointData = pointList.get(i);
            double cha = pointMax.getBiwen() - pointData.getBiwen();

            if(cha > dt){
                //找到下一个pointMax-pointData<0.7dt的点或者超出列表
                Boolean flag = true;
                while(flag){
                    i++;
                    if (i >= pointList.size()) {
                        break;
                    }
                    PointData pointDataNext1 = pointList.get(i);
                    double cha1 = pointMax.getBiwen() - pointDataNext1.getBiwen();
                    if(cha1<0.7*dt){
                        //满足8小时以上，吹灰+1
                        boolean f8 = DateUtil.isWithinTenMinutes(pointData.getTime(),pointDataNext1.getTime(),60*FUHE_START);
                        if(f8){
                            count++;
                            System.out.println("开始时间："+pointData.getTime()+"——————————结束时间："+pointDataNext1.getTime());
                            flag = false;
                        }else{
                            //不满足8小时，则判断是否满足6小时以上
                            boolean f6 = DateUtil.isWithinTenMinutes(pointData.getTime(),pointDataNext1.getTime(),60*FUHE_END);
                            //找到下一个pointMax-pointData<0.5dt的点或者超出列表
                            if(f6){
                                boolean flag2 = true;
                                while (flag2){
                                    i++;
                                    if (i >= pointList.size()) {
                                        break;
                                    }
                                    PointData pointDataNext2 = pointList.get(i);
                                    double cha2 = pointMax.getBiwen() - pointDataNext2.getBiwen();
                                    if(cha2<0.5*dt){
                                        count++;;
                                        System.out.println("开始时间："+pointData.getTime()+"——————————结束时间："+pointDataNext1.getTime());
                                        flag = false;
                                        flag2 = false;
                                    }
                                }
                            }else {
                                i++;
                                flag = false;
                            }
                        }
                    }
                    if(i >= pointList.size()-1){
                        i++;
                        flag = false;
                    }
                }
            }else{
                i++;
            }
        }
        if(count<1){
            alfa += 0.2;
            alfaMap.put(key,alfa);
            method1(pointList, key);
        }else if(count >= 15){
            alfa -= 0.2;
            alfaMap.put(key,alfa);
            method1(pointList, key);
        }
        return alfa;

    }


    //拟合效果好
    private static double method2(List<PointData> pointList,String xishu,String key) {
        List<PointData> filterList = pointList.stream()
                .filter(point -> point.getFuhe() >= 1900 && point.getFuhe() < 2000).collect(Collectors.toList());
        Optional<PointData> t = filterList.stream().max(Comparator.comparingDouble(PointData::getBiwen));
        double biwenMax = filterList.stream().max(Comparator.comparingDouble(PointData::getBiwen)).get().getBiwen();
        double biwenMin = filterList.stream().min(Comparator.comparingDouble(PointData::getBiwen)).get().getBiwen();
        Double alfa = alfaMap.get(key);
        double dt = (biwenMax-biwenMin) * alfa;

        String[] params = xishu.split(",");
        double param1 =new BigDecimal(params[0]).doubleValue();
        double param2 =new BigDecimal(params[1]).doubleValue();
        double param3 =new BigDecimal(params[2]).doubleValue();

        int count = 0 ;
        for (int i = 0; i < pointList.size(); ) {
            PointData pointData = pointList.get(i);
            double x=pointData.getFuhe();
            //计算理论最大温度
            double maxT = x*x*param1+x*param2+param3;
            double cha = maxT - pointData.getBiwen();
            if(cha > dt){
                //找到下一个pointMax-pointData<0.7dt的点或者超出列表
                Boolean flag = true;
                while(flag){
                    i++;
                    if (i >= pointList.size()) {
                        break;
                    }
                    PointData pointDataNext1 = pointList.get(i);
                    double x1=pointDataNext1.getFuhe();
                    maxT = x1*x1*param1+x1*param2+param3;
                    double cha1 = maxT - pointDataNext1.getBiwen();
                    if(cha1<0.7*dt){
                        //满足8小时以上，吹灰+1
                        boolean f8 = DateUtil.isWithinTenMinutes(pointData.getTime(),pointDataNext1.getTime(),60*NOFUHE_START);
                        if(f8){
                            count++;
                            System.out.println("xishu:"+xishu+"-----------开始时间："+pointData.getTime()+"——————————结束时间："+pointDataNext1.getTime());
                            flag = false;
                        }else{
                            //不满足8小时，则判断是否满足6小时以上
                            boolean f6 = DateUtil.isWithinTenMinutes(pointData.getTime(),pointDataNext1.getTime(),60*NOFUHE_END);
                            //找到下一个pointMax-pointData<0.5dt的点或者超出列表
                            if(f6){
                                boolean flag2 = true;
                                while (flag2){
                                    i++;
                                    if (i >= pointList.size()) {
                                        break;
                                    }
                                    PointData pointDataNext2 = pointList.get(i);
                                    double x2=pointDataNext2.getFuhe();
                                    maxT = x2*x2*param1+x2*param2+param3;
                                    double cha2 = maxT - pointDataNext2.getBiwen();
                                    if(cha2<0.5*dt){
                                        count++;
                                        System.out.println("xishu:"+xishu+"——————————开始时间："+pointData.getTime()+"——————————结束时间："+pointDataNext1.getTime());
                                        flag = false;
                                        flag2 = false;
                                    }
                                    if(i >= pointList.size()-1){
                                        i++;
                                        flag = false;
                                        flag2 = false;
                                    }
                                }
                            }else {
                                i++;
                                flag = false;
                            }
                        }
                    }
                    if(i >= pointList.size()-1){
                        i++;
                        flag = false;
                        break;
                    }
                }
            }else{
                i++;
            }
        }
        if(count<1){
            alfa -= 0.1;
            alfaMap.put(key,alfa);
            method2(pointList,xishu,key);
        }else if(count >= 20){
            alfa += 0.1;
            alfaMap.put(key,alfa);
            method2(pointList, xishu, key);
        }
        return alfa;

    }




}
